1bbe93
@@ -57,26 +57,61 @@
public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
 		filterChain.doFilter(request, responseWrapper);
 
 		byte[] body = responseWrapper.toByteArray();
-		String responseETag = generateETagHeaderValue(body);
-		response.setHeader(HEADER_ETAG, responseETag);
-
-		String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
-		if (responseETag.equals(requestETag)) {
-			if (logger.isTraceEnabled()) {
-				logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
+		int statusCode = responseWrapper.getStatusCode();
+
+		if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {
+			String responseETag = generateETagHeaderValue(body);
+			response.setHeader(HEADER_ETAG, responseETag);
+
+			String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
+			if (responseETag.equals(requestETag)) {
+				if (logger.isTraceEnabled()) {
+					logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
+				}
+				response.setContentLength(0);
+				response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+			}
+			else {
+				if (logger.isTraceEnabled()) {
+					logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag +
+							"], sending normal response");
+				}
+				copyBodyToResponse(body, response);
 			}
-			response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 		}
 		else {
 			if (logger.isTraceEnabled()) {
-				logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag +
-						"], sending normal response");
+				logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");
 			}
-			response.setContentLength(body.length);
+			copyBodyToResponse(body, response);
+		}
+	}
+
+	private void copyBodyToResponse(byte[] body, HttpServletResponse response) throws IOException {
+		response.setContentLength(body.length);
+		if (body.length > 0) {
 			FileCopyUtils.copy(body, response.getOutputStream());
 		}
 	}
 
+	/**
+	 * Indicates whether the given request and response are eligible for ETag generation.
+	 *
+	 * <p>Default implementation returns {@code true} for response status codes in the {@code 2xx} series.
+	 *
+	 * @param request the HTTP request
+	 * @param response the HTTP response
+	 * @param responseStatusCode the HTTP response status code
+	 * @param responseBody the response body
+	 * @return {@code true} if eligible for ETag generation; {@code false} otherwise
+	 */
+	protected boolean isEligibleForEtag(HttpServletRequest request,
+			HttpServletResponse response,
+			int responseStatusCode,
+			byte[] responseBody) {
+		return (responseStatusCode >= 200 && responseStatusCode < 300);
+	}
+
 	/**
 	 * Generate the ETag header value from the given response body byte array. <p>The default implementation generates an
 	 * MD5 hash.
@@ -105,10 +140,36 @@
public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
 
 		private PrintWriter writer;
 
+		private int statusCode = -1;
+
 		private ShallowEtagResponseWrapper(HttpServletResponse response) {
 			super(response);
 		}
 
+		@Override
+		public void setStatus(int sc) {
+			super.setStatus(sc);
+			this.statusCode = sc;
+		}
+
+		@Override
+		public void setStatus(int sc, String sm) {
+			super.setStatus(sc, sm);
+			this.statusCode = sc;
+		}
+
+		@Override
+		public void sendError(int sc) throws IOException {
+			super.sendError(sc);
+			this.statusCode = sc;
+		}
+
+		@Override
+		public void sendError(int sc, String msg) throws IOException {
+			super.sendError(sc, msg);
+			this.statusCode = sc;
+		}
+
 		@Override
 		public ServletOutputStream getOutputStream() {
 			return this.outputStream;
@@ -135,6 +196,10 @@
public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
 			resetBuffer();
 		}
 
+		private int getStatusCode() {
+			return statusCode;
+		}
+
 		private byte[] toByteArray() {
 			return this.content.toByteArray();
 		}
